package com.liuyi.netty.im.server.util;

import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSONObject;
import com.liuyi.netty.im.commons.codes.CodeConst;
import com.liuyi.netty.im.commons.codes.data.DataEvent;
import com.liuyi.netty.im.commons.codes.data.event.Message;
import com.liuyi.netty.im.commons.codes.data.event.Session;
import com.liuyi.netty.im.commons.codes.data.event.message.playload.Playload;
import com.liuyi.netty.im.commons.codes.data.event.message.playload.PlayloadSys;
import com.liuyi.netty.im.commons.codes.data.event.message.playload.PlayloadText;
import com.liuyi.netty.im.commons.codes.unusual.LimException;
import com.liuyi.netty.im.server.config.LimManage;
import com.liuyi.netty.im.server.handle.message.SendMessageProcessor;
import io.netty.channel.Channel;

import java.util.Collections;
import java.util.List;

/**
 * 供集成使用的工具类
 *
 * @author Mr.Fmy
 * @since 2021/12/25
 */
public class ImUtil {


    /**
     * 发送消息（一般用于未发送消息重新发送）
     *
     * @param message 消息
     */
    public static <T extends Playload> void sendMessage(Message<T> message) {
        ImUtil.sendCluster(CodeConst.Event.MESSAGE_RECEIVED, CodeConst.EventToType.USER, message.getToId(), message);
    }

    /**
     * 发送C2C消息
     * 四条事件通知
     * 例如 A 发送 B
     * A 会话列表刷新通知
     * B 会话列表刷新通知
     * A 接收消息通知
     * B 接收消息通知
     */
    public static <T extends Playload> void c2cMessage(String messageId, Long createTime, String fromId, String toId, CodeConst.MsgType msgType, T playload) {
        Message<T> inMessage = builderC2CMessage(CodeConst.MsgFlow.IN, messageId, createTime, fromId, toId, msgType, playload);
        Message<T> outMessage = builderC2CMessage(CodeConst.MsgFlow.OUT, messageId, createTime, fromId, toId, msgType, playload);
        sessionListRefresh(toId, LimManage.getSessionService().setLastMessage(toId, inMessage.getSessionId(), inMessage));
        sessionListRefresh(fromId, LimManage.getSessionService().setLastMessage(fromId, outMessage.getSessionId(), outMessage));
        sendCluster(CodeConst.Event.MESSAGE_RECEIVED, CodeConst.EventToType.USER, toId, inMessage);
        sendCluster(CodeConst.Event.MESSAGE_RECEIVED, CodeConst.EventToType.USER, fromId, outMessage);
    }


    /**
     * 发送群聊消息
     *
     * @param messageId  消息id
     * @param createTime 创建时间
     * @param fromId     发送人
     * @param groupId    群组id
     * @param msgType    消息类型
     * @param playload   消息体
     */
    public static <T extends Playload> void groupMessage(String messageId, Long createTime, String fromId, String groupId, CodeConst.MsgType msgType, T playload) {
        List<String> userIds = LimManage.getRelationChainService().listByGroupId(groupId);
        Object groupProfile = LimManage.getRelationChainService().groupProfile(fromId, groupId);
        userIds.forEach(userId -> {
            Message<T> message;
            if (userId.equals(fromId)) {
                message = builderGroupMessage(groupId, groupProfile, CodeConst.MsgFlow.OUT, messageId, createTime, fromId, userId, msgType, playload);
            } else {
                message = builderGroupMessage(groupId, groupProfile, CodeConst.MsgFlow.IN, messageId, createTime, fromId, userId, msgType, playload);
            }
            sessionListRefresh(userId, LimManage.getSessionService().setLastMessage(userId, message.getSessionId(), message));
            sendCluster(CodeConst.Event.MESSAGE_RECEIVED, CodeConst.EventToType.USER, userId, message);
        });
    }

    public static void sysMessage(String sessionId, String toId, String text) {
        sysMessage(IdUtil.fastSimpleUUID(), System.currentTimeMillis(), sessionId, toId, text);
    }

    /**
     * 系统消息
     *
     * @param messageId  消息id
     * @param createTime 创建时间
     * @param sessionId  会话id
     * @param toId       为id
     * @param text       文本
     */
    public static void sysMessage(String messageId, Long createTime, String sessionId, String toId, String text) {
        Message<PlayloadSys> message = new Message<>();
        message.setMessageId(messageId);
        message.setSessionId(sessionId);
        message.setCreateTime(createTime);
        message.setToId(toId);
        message.setMsgFlow(CodeConst.MsgFlow.IN);
        message.setMsgType(CodeConst.MsgType.SYS);
        message.setPlayload(PlayloadSys.Builder.builder().text(text).build());
        message.setSessionType(CodeConst.SessionType.C2C);
        sendMessage(message);
    }

    /**
     * 创建群聊提醒消息
     *
     * @param groupId 群组id
     * @param text    提醒语
     */
    public static <T extends Playload> void createGroupMessage(String groupId, String text) {
        groupMessage(IdUtil.fastSimpleUUID(), System.currentTimeMillis(), null, groupId, CodeConst.MsgType.GROUP_SYS, PlayloadText.PlayloadTextBuilder.aPlayloadText().text(text).build());
    }

    /**
     * 有人加入群聊提醒消息
     *
     * @param groupId 群组id
     * @param text    欢迎语
     */
    public static <T extends Playload> void addGroupMessage(String groupId, String text) {
        groupMessage(IdUtil.fastSimpleUUID(), System.currentTimeMillis(), null, groupId, CodeConst.MsgType.GROUP_SYS, PlayloadText.PlayloadTextBuilder.aPlayloadText().text(text).build());
    }

    /**
     * 有人离开群聊提醒消息
     *
     * @param groupId 群组id
     * @param text    离开提示语
     */
    public static <T extends Playload> void quitGroupMessage(String groupId, String text) {
        groupMessage(IdUtil.fastSimpleUUID(), System.currentTimeMillis(), null, groupId, CodeConst.MsgType.GROUP_SYS, PlayloadText.PlayloadTextBuilder.aPlayloadText().text(text).build());
    }

    /**
     * 解散群聊提醒消息
     *
     * @param groupId 群组id
     * @param text    提示语
     */
    public static <T extends Playload> void dissolveGroupMessage(String groupId, String text) {
        groupMessage(IdUtil.fastSimpleUUID(), System.currentTimeMillis(), null, groupId, CodeConst.MsgType.GROUP_SYS, PlayloadText.PlayloadTextBuilder.aPlayloadText().text(text).build());
    }

    /**
     * 会话列表刷新
     *
     * @param toId    接收方
     * @param session 会话
     */
    public static void sessionListRefresh(String toId, Session session) {
        sessionListRefresh(toId, Collections.singletonList(session));
    }

    /**
     * 会话列表刷新
     *
     * @param toId     接收方
     * @param sessions 会话列表
     */
    public static void sessionListRefresh(String toId, List<Session> sessions) {
        sendCluster(CodeConst.Event.SESSION_LIST_REFRESH, CodeConst.EventToType.USER, toId, sessions);
    }

    /**
     * 本地直接根据 Channel 发送
     *
     * @param channel   channel
     * @param dataEvent 消息体
     */
    public static void send(Channel channel, DataEvent dataEvent) {
        if (!LimManage.getSendMessageProcessor().offer(new SendMessageProcessor.Message(channel, dataEvent))) {
            throw new LimException("Message Offer Fail");
        }
    }

    /**
     * 集群发送
     *
     * @param event  事件类型
     * @param toType 接收方类型
     * @param toId   接收方
     * @param data   携带数据
     */
    public static void sendCluster(CodeConst.Event event, CodeConst.EventToType toType, String toId, Object data) {
        DataEvent dataEvent = new DataEvent();
        dataEvent.setEvent(event);
        dataEvent.setToType(toType);
        dataEvent.setToId(toId);
        dataEvent.setData(data);
        //广播消息
        LimManage.getClusterSend().broadcast(dataEvent);
    }

    /**
     * 解码playload
     *
     * @param playload playload
     * @param tClass   t类
     * @return {@link T}
     */
    public static <T extends Playload> T decodePlayload(Object playload, Class<T> tClass) {
        return JSONObject.parseObject(encodePlayload(playload), tClass);
    }

    /**
     * 解码playload
     *
     * @param playload playload
     * @param tClass   t类
     * @return {@link T}
     */
    public static <T extends Playload> T decodePlayload(byte[] playload, Class<T> tClass) {
        return JSONObject.parseObject(playload, tClass);
    }


    /**
     * 编码
     *
     * @param playload playload
     * @return {@link byte[]}
     */
    public static byte[] encodePlayload(Object playload) {
        if (playload == null) {
            return null;
        }
        return JSONObject.toJSONBytes(playload);
    }

    private static <T extends Playload> Message<T> builderC2CMessage(CodeConst.MsgFlow flow, String messageId, Long createTime, String fromId, String toId, CodeConst.MsgType msgType, T playload) {
        String sessionId;
        if (flow.equals(CodeConst.MsgFlow.IN)) {
            sessionId = ImHelp.sessionId(CodeConst.SessionType.C2C, fromId);
        } else {
            sessionId = ImHelp.sessionId(CodeConst.SessionType.C2C, toId);
        }
        Message<T> message = new Message<>();
        message.setMessageId(messageId);
        message.setCreateTime(createTime);
        message.setFromProfile(LimManage.getRelationChainService().c2cProfile(fromId, toId));
        message.setFromId(fromId);
        message.setToId(toId);
        message.setMsgFlow(flow);
        message.setMsgType(msgType);
        message.setPlayload(playload);
        message.setSessionId(sessionId);
        message.setSessionType(CodeConst.SessionType.C2C);
        return message;
    }

    private static <T extends Playload> Message<T> builderGroupMessage(String groupId, Object fromProfile, CodeConst.MsgFlow flow, String messageId, Long createTime, String fromId, String toId, CodeConst.MsgType msgType, T playload) {
        String sessionId;
        if (flow.equals(CodeConst.MsgFlow.IN)) {
            sessionId = ImHelp.sessionId(CodeConst.SessionType.GROUP, groupId);
        } else {
            sessionId = ImHelp.sessionId(CodeConst.SessionType.GROUP, groupId);
        }
        Message<T> message = new Message<>();
        message.setMessageId(messageId);
        message.setCreateTime(createTime);
        message.setFromProfile(fromProfile);
        message.setFromId(fromId);
        message.setToId(toId);
        message.setMsgFlow(flow);
        message.setMsgType(msgType);
        message.setPlayload(playload);
        message.setSessionId(sessionId);
        message.setSessionType(CodeConst.SessionType.GROUP);
        return message;
    }

}
